
/****************************************************************************
 *
 *  ALi (Shanghai) Corporation, All Rights Reserved. 2007 Copyright (C)
 *
 *  File: ogg.c
 *
 *  Description: This file describes ogg file operations.
 *
 *  History:
 *      Date        	Author      	Version  		Comment
 *      ====        	======      =======  	=======
 *  1.  2007.5.22  Martin_Xia  	0.0.1  		
 *
 ****************************************************************************/

#include <types.h>
#include <retcode.h>
#include <api/libc/printf.h>

#include <api/libc/string.h>
#include <osal/osal.h>
#include <hld/snd/snd_dev.h>
#include <hld/snd/snd.h>//We need to call STC APIs from sound driver
#include <hld/deca/deca.h>

#include <api/libfs2/stat.h>
#include <api/libfs2/dirent.h>
#include <api/libfs2/statvfs.h>
#include <api/libfs2/unistd.h>
#include <api/libfs2/stdio.h>


#include "../../../play_engine_common.h"
#include <api/libmp/pe.h>
#include "../../plugin.h"
#include "ivorbiscodec.h"
#include "ivorbisfile.h"
#include "ogg_interface.h"


/**********************Global  Variables***********************************/
//define the input plugin function table
_Audio_InputPlugin(ogg_input_plugin) =
{
	NULL,
	NULL,
	NULL,
	ogg_init,
	NULL,
	NULL,
	ogg_is_our_file,
	ogg_play_file,
	ogg_stop,
	ogg_pause,
	ogg_seek,
	NULL,
	ogg_get_time,
	NULL,
	NULL,
	ogg_cleanup,
	NULL,
	NULL,
	NULL,
	ogg_get_song_info,
	ogg_get_decoder_info
};
static struct ogg_engine_parameter ogg_engine;
static OggFile *ogg_file;
static UINT32 ogg_dup_samplerate;

#ifdef ENABLE_PE_CACHE
int ogg_cache_id;
int ogg_info_cache_id = -1;
#endif

/////////////////////////////////////////////////////////////////////////////
// ogg_init
//
// Description:
//	Init function
//
// Arguments:
//	None
//	
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////

static int ogg_init(struct pe_music_cfg *pe_music_cfg)
{
	//allocate buffer for ogg_file
	ogg_file = (OggFile*)MALLOC(sizeof(OggFile));
	if (!ogg_file)
	{
		PE_DBG_PRINTF("MusicEngine: ogg_init() MALLOC failed! \n");
		return -1;
	}

	MEMSET(ogg_file, 0, sizeof(OggFile));

	if (pe_music_cfg->pcm_out_buff_size >= 4608)
	{
		ogg_file->pcm_out_buff = pe_music_cfg->pcm_out_buff;
	}
	else
	{
		FREE(ogg_file);
		ogg_file = NULL;
		return -1;
	}
	
	if (pe_music_cfg->processed_pcm_buff_size >= 9216)
	{
		ogg_file->processed_pcm_buff = pe_music_cfg->processed_pcm_buff;
	}
	else
	{
		FREE(ogg_file);
		ogg_file = NULL;
		return -1;
	}
	
	return 0;
}

/////////////////////////////////////////////////////////////////////////////
// ogg_cleanup
//
// Description:
//	cleanup function
//
// Arguments:
//	None
//	
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////

static void ogg_cleanup(void)
{
	if (ogg_file)
	{
		FREE(ogg_file);
		ogg_file = NULL;
	}
	
	return;
}
/////////////////////////////////////////////////////////////////////////////
// ogg_is_our_file
//
// Description:
//	To judge a file is ogg file or not
//
// Arguments:
//	IN  char *filename  	file name
//	
// Return Value:
//	True		The file is ogg
//  False		The file is not ogg
//
/////////////////////////////////////////////////////////////////////////////

static int ogg_is_our_file(char *filename)
{

	char 							*ext;
	FILE								*file;
	unsigned long						file_length;
	OggVorbis_File					temp_ogg;

	//check parameters
	if ((!filename) || (!ogg_file))
	{
		PE_DBG_PRINTF("MusicEngine: ogg_is_our_file() invalid parameter filename or ogg_file! \n");
		return FALSE;
	}

	
	ext = strrchr(filename, (int)'.');
	if (ext)
	{
		if (!strncasecmp(ext, ".OGG", 4))
		{
#ifndef ENABLE_PE_CACHE          
			file = fopen(filename, "rb");                       // is our file, open file
			if (!file)
			{
				return FALSE;
			}
#else
            ogg_info_cache_id = pe_cache_open(filename, NULL, OGG_CACHE_SIZE, OGG_BLOCK_SIZE);
            if (ogg_info_cache_id < 0)
            {
                PE_DBG_PRINTF("<%d> <%s> pe_cache_open failed!\n", __LINE__, __FUNCTION__);
                return FALSE;
            }
            file = (FILE *)(((UINT32)ogg_info_cache_id | PE_CACHE_ID_TAG));
#endif
			//here the pe_cache API is not used in the FS
			if(ov_test(file, &temp_ogg, NULL, 0) < 0)
			{
				PE_DBG_PRINTF("Input does not appear to be an Ogg bitstream.\n");
#ifndef ENABLE_PE_CACHE                
				fclose(file);                                   // if test not ogg, close file
#else
                pe_cache_close(ogg_info_cache_id);
				ogg_info_cache_id = -1;
#endif
 				return FALSE;
			}

			ov_clear(&temp_ogg);//use the close when ov_tset return 0
			return TRUE;
		}
	}

	PE_DBG_PRINTF("Input does not appear to be an Ogg bitstream.\n");
	return FALSE;

}
/////////////////////////////////////////////////////////////////////////////
// low_sprt_ogg_output
//
// Description:
//	The ogg play task. It fetch the data from the file. Then fill it to the decoder buffer.
//  When reaching the end of the file, it calls the call back function to notify the app.
//
// Arguments:
//	IN  mp_callback_func cb  	The pointer to call back function
//	
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////

UINT32 ogg_avoid_under_run;
UINT32 prev_sp_l = 0,prev_sp_r = 0;

static void low_sprt_ogg_output(struct snd_device * sound_dev, struct pcm_output * pcm)
{
//	struct deca_s3601_private *priv=(struct deca_s3601_private*)sound_dev->priv ;
	UINT16 pcm_frame_header[4];
	UINT32 temp, stc ;
	UINT8 timeout;
	//config pcm frame header
	pcm_frame_header[0]=0 ;
	pcm_frame_header[1]=0 ;
	pcm_frame_header[2]=0 ;
	pcm_frame_header[3]=0 ;

	// write frame_len into frame header
	if(pcm->samp_num&0x00000003)
	{
		//DECA_PRINTF("err sample num = %d \n",pcm->samp_num);
		//DECA_ASSERT(0);
		goto CUR_FRM_FINISHED ;
	}
	if ( sys_ic_get_chip_id() == ALI_S3811 )
	    pcm_frame_header[0]=pcm->samp_num/2+2;	
	else if(pcm->ch_num<=2)
	{
		pcm_frame_header[0]=pcm->samp_num+1;
	}	
	else if(pcm->ch_num==8)
	{
		pcm_frame_header[0]=pcm->samp_num*4+4;
	}
	else
	{
		ASSERT(0);
	}	

	
	if( (SND_STATE_PLAY != sound_dev->flags) && (0 == ogg_avoid_under_run))
	{
		UINT32 pcm_buf_frm_num;
//		DECA_PRINTF("audio mode %d  len %d bit rate %d id %d layer %d\n", priv->aud_mode, priv->frm_len, priv->bitrate, priv->id, priv->layer);

	if(pcm->ch_num==1)
		sound_dev->hardware=2;
	else
		sound_dev->hardware = pcm->ch_num;
		if(RET_SUCCESS!=snd_config(sound_dev,ogg_dup_samplerate,pcm->samp_num,24))
		{
			//DECA_PRINTF("Cfg aud h/w FAILED.\n");
			goto CUR_FRM_FINISHED ;
		}
		snd_write_pcm_data(sound_dev,pcm, (UINT32*)pcm_frame_header);
   		ogg_avoid_under_run = 5;	
		if(0==ogg_avoid_under_run)
			snd_start(sound_dev);
		//DECA_PRINTF("preserve %d pcm frm\n", priv->avoid_under_run+1);
		goto CUR_FRM_FINISHED ;
	}

	if(0!=ogg_avoid_under_run)
	{
		//DECA_PRINTF("priv->avoid_under_run is not 0 ! \r\n");
		snd_write_pcm_data(sound_dev, pcm, (UINT32*)pcm_frame_header);
		ogg_avoid_under_run--;
		if(0==ogg_avoid_under_run)snd_start(sound_dev);
		goto CUR_FRM_FINISHED ;
	}
       
	while(RET_SUCCESS!=snd_request_pcm_buff(sound_dev, pcm_frame_header[0]))
	{
		osal_task_sleep(24);
	}
 	snd_write_pcm_data(sound_dev, pcm, (UINT32*)pcm_frame_header);
    
CUR_FRM_FINISHED :
	return ;
}

/////////////////////////////////////////////////////////////////////////////
// process_ogg_pcm
//
// Description:
//	The ogg play task. It fetch the data from the file. Then fill it to the decoder buffer.
//  When reaching the end of the file, it calls the call back function to notify the app.
//
// Arguments:
//	IN  mp_callback_func cb  	The pointer to call back function
//	
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////
/*
#define OGG_PCM_DATA_LEN (1024*1024)
static char g_ogg_pcb_data_buffer[1025*1024];
static int g_ogg_pcm_buffer_index = 0;
*/

static char* g_temp_pcm_buff;
static UINT32 g_pcm_sample_num;
static UINT32 g_pcm_current_using = 0;
#define ON_PCM_BUFF_LEN     1152

void process_ogg_pcm(struct snd_device * sound_dev, int bytes_readed, int *processed_pcm)
{

	INT16							*out_buf_temp;
	UINT32							out_samp_num;
	UINT32							i;
	struct pcm_output					pcm;
    int bytes_left = 0;

    UINT32 bytes_per_sample= 0;
    UINT32 chl_num = 0;
    UINT32 sample_rate = 0;
    UINT32 bits_per_sample = 0;
    UINT32 pcm_data_len_byte = 0;

	//we process sample rate for martching our sound device. 
	//note : the pcm code may not integrity, I don't know it works or not, under testing 
 	out_buf_temp = (INT16*)ogg_file->pcm_out_buff;
	out_samp_num = (bytes_readed /2) / ogg_file->channel;

    if(out_samp_num > ON_PCM_BUFF_LEN)
        return; 
    
    if(g_pcm_sample_num + out_samp_num < ON_PCM_BUFF_LEN)
    {
        MEMCPY((char*)(g_temp_pcm_buff+g_pcm_current_using),out_buf_temp,bytes_readed);
        g_pcm_current_using += bytes_readed;
        g_pcm_sample_num += out_samp_num;
        return;
    }
    else 
    {
        MEMCPY((char*)(g_temp_pcm_buff+g_pcm_current_using),out_buf_temp,(ON_PCM_BUFF_LEN-g_pcm_sample_num)*ogg_file->channel*2);
        bytes_left = bytes_readed - (ON_PCM_BUFF_LEN-g_pcm_sample_num)*ogg_file->channel*2;
    }
    g_pcm_current_using  = 0;
    g_pcm_sample_num = 0;
    
    out_samp_num = ON_PCM_BUFF_LEN;
    pcm.samp_num = ON_PCM_BUFF_LEN;
    out_buf_temp = (INT16 *)g_temp_pcm_buff;

    //ADD NEW API
    bytes_per_sample = 2;
    chl_num = (ogg_file->channel > 2) ? 2: ogg_file->channel;
    sample_rate = ogg_file->samplerate;
    bits_per_sample = 16;
    pcm_data_len_byte = (ON_PCM_BUFF_LEN)*ogg_file->channel*bytes_per_sample;

    deca_process_pcm_samples(pcm_data_len_byte,out_buf_temp,sample_rate,chl_num,bits_per_sample);//NEW API
    

#if 0       // comment old method
	//pcm.samp_num = out_samp_num;
	pcm.ch_num = (ogg_file->channel > 2) ? 2: ogg_file->channel;
	pcm.ch_left = (UINT32 *)processed_pcm;
	pcm.ch_right = (UINT32 *)(processed_pcm + 1152);
	pcm.raw_data_start = NULL;
	pcm.raw_data_len = 0;	
		
	if(24000==ogg_file->samplerate||16000==ogg_file->samplerate||22050==ogg_file->samplerate)
	{
		ogg_dup_samplerate = ogg_file->samplerate<<1;
		if(out_samp_num <= 576)
		{
			if(pcm.ch_num == 1) //(SINGLE_CHANNEL)
			{
				snd_set_duplicate(sound_dev, SND_DUP_L);
				for(i=0; i<out_samp_num; i++)
				{
					*(processed_pcm+(i*2)) = (INT32)out_buf_temp[i] << 8;
					*(processed_pcm+(i*2 + 1)) = (INT32)out_buf_temp[i] << 8;
				}

			}
			else
			{
				snd_set_duplicate(sound_dev, SND_DUP_NONE);
				for(i=0; i<out_samp_num; i++)
				{
					*(processed_pcm+(i*2)) = (INT32)out_buf_temp[i*ogg_file->channel] << 8;
					*(processed_pcm+(i*2 + 1)) = (INT32)out_buf_temp[i*ogg_file->channel] << 8;
			 		*(processed_pcm + 1152 + (i*2)) = (INT32)out_buf_temp[i*ogg_file->channel+1] << 8;
					*(processed_pcm + 1152 + (i*2 + 1))= (INT32)out_buf_temp[i*ogg_file->channel+1] << 8;
				}

			}
			pcm.samp_num = out_samp_num<<1;
			low_sprt_ogg_output(sound_dev, &pcm);

		}
		else
		{
			UINT32 start_point;
			UINT32 full_count, count;
			full_count = out_samp_num  / 576;
			count = out_samp_num  % 576;
			
			if(pcm.ch_num == 1) //(SINGLE_CHANNEL)
			{
				snd_set_duplicate(sound_dev, SND_DUP_L);
				
				for (start_point = 0; start_point < full_count; start_point++)
				{
					for(i=0; i<576; i++)
					{
						*(processed_pcm+(i*2)) = (INT32)out_buf_temp[start_point * 576 + i] << 8;
						*(processed_pcm+(i*2 + 1)) = (INT32)out_buf_temp[start_point * 576 + i] << 8;
					}
					pcm.samp_num = 1152;
					low_sprt_ogg_output(sound_dev, &pcm);

				}

				for (i=0; i<count; ++i)
				{
					*(processed_pcm+(i*2)) = (INT32)out_buf_temp[start_point*576 + i] << 8;
					*(processed_pcm+(i*2 + 1)) = (INT32)out_buf_temp[start_point*576 + i] << 8;
				}
				if (count)
				{
					pcm.samp_num = count * 2;
					low_sprt_ogg_output(sound_dev, &pcm);
				}

			}
			else
			{
				snd_set_duplicate(sound_dev, SND_DUP_NONE);
				for (start_point = 0; start_point < full_count; start_point++)
				{
					for(i=0; i<576; i++)
					{
						*(processed_pcm+(i*2)) = (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel] << 8;
						*(processed_pcm+(i*2 + 1)) = (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel] << 8;
				 		*(processed_pcm + 1152 + (i*2)) = (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel + 1] << 8;
						*(processed_pcm + 1152 + (i*2 + 1))= (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel + 1] << 8;
					}
					pcm.samp_num = 1152;
					low_sprt_ogg_output(sound_dev, &pcm);
				}

				for (i=0; i<count; ++i)
				{
					*(processed_pcm+(i*2)) = (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel] << 8;
					*(processed_pcm+(i*2 + 1)) = (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel] << 8;
				 	*(processed_pcm + 1152 + (i*2)) = (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel + 1] << 8;
					*(processed_pcm + 1152 + (i*2 + 1))= (INT32)out_buf_temp[start_point*576*ogg_file->channel + i*ogg_file->channel + 1] << 8;
				}

				if (count)
				{
					pcm.samp_num = count * 2;
					low_sprt_ogg_output(sound_dev, &pcm);
				}
			}

		}
		if(bytes_left > 0)
        {
            MEMCPY((char*)(g_temp_pcm_buff),(char*)(ogg_file->pcm_out_buff+(bytes_readed-bytes_left)),bytes_left);
            g_pcm_current_using = bytes_left;
            g_pcm_sample_num = bytes_left/ogg_file->channel/2;
        }
		return;
	}

	if(12000==ogg_file->samplerate||11025==ogg_file->samplerate||8000==ogg_file->samplerate)
	{
		INT32 cur_sp = 0;
//		static UINT32 prev_sp_l = 0,prev_sp_r = 0;
		ogg_dup_samplerate = ogg_file->samplerate<<2;
		if(out_samp_num<=288)
		{
			if(pcm.ch_num == 1) //(SINGLE_CHANNEL==ogg_file->aud_mode)
			{
				snd_set_duplicate(sound_dev, SND_DUP_L);
				for (i=0; i<out_samp_num; ++i)
				{
					cur_sp = (INT32)out_buf_temp[i] << 8;
					*(processed_pcm+(i*4)) = prev_sp_l+(cur_sp-prev_sp_l)/4;
					*(processed_pcm+(i*4 + 1)) = prev_sp_l+(cur_sp-prev_sp_l)/2;
					*(processed_pcm+(i*4 + 2)) = prev_sp_l+(cur_sp-prev_sp_l)*3/4;
					*(processed_pcm+(i*4 + 3)) = cur_sp;
					prev_sp_l = cur_sp;
				}				
			}
			else
			{
				snd_set_duplicate(sound_dev, SND_DUP_NONE);
				for (i=0; i<out_samp_num; ++i)
				{
					cur_sp = (INT32)out_buf_temp[i*ogg_file->channel] << 8;
					*(processed_pcm+(i*4)) = prev_sp_l+(cur_sp-prev_sp_l)/4;
					*(processed_pcm+(i*4 + 1)) = prev_sp_l+(cur_sp-prev_sp_l)/2;
					*(processed_pcm+(i*4 + 2)) = prev_sp_l+(cur_sp-prev_sp_l)*3/4;
					*(processed_pcm+(i*4 + 3)) = cur_sp;
					prev_sp_l = cur_sp;
						
					cur_sp = (INT32)out_buf_temp[i*ogg_file->channel+1] << 8;
				 	*(processed_pcm + 1152 + (i*4)) = prev_sp_r+(cur_sp-prev_sp_r)/4;
					*(processed_pcm + 1152 + (i*4 + 1))= prev_sp_r+(cur_sp-prev_sp_r)/2;
					*(processed_pcm + 1152 + (i*4 + 2))= prev_sp_r+(cur_sp-prev_sp_r)*3/4;
					*(processed_pcm + 1152 + (i*4 + 3))= cur_sp;
					prev_sp_r = cur_sp;
				}
			}
			
			pcm.samp_num = out_samp_num<<2;
			low_sprt_ogg_output(sound_dev, &pcm);

		}
		else //out_samp_num>288
		{
			UINT32 start_point;
			UINT32 full_count, count;
			
			full_count = out_samp_num  / 288;
			count = out_samp_num  % 288;
			
			if(pcm.ch_num == 1) //(SINGLE_CHANNEL)
			{
				snd_set_duplicate(sound_dev, SND_DUP_L);
				
				for (start_point = 0; start_point < full_count; start_point++)
				{
					for(i=0; i<288; i++)
					{
						cur_sp = (INT32)out_buf_temp[start_point*288+i] << 8;
						*(processed_pcm+(i*4)) = prev_sp_l+(cur_sp-prev_sp_l)/4;
						*(processed_pcm+(i*4 + 1)) = prev_sp_l+(cur_sp-prev_sp_l)/2;
						*(processed_pcm+(i*4 + 2)) = prev_sp_l+(cur_sp-prev_sp_l)*3/4;
						*(processed_pcm+(i*4 + 3)) = cur_sp;
						prev_sp_l = cur_sp;
					}
					pcm.samp_num = 1152;
					low_sprt_ogg_output(sound_dev, &pcm);

				}

				for (i=0; i<count; ++i)
				{
					cur_sp = (INT32)out_buf_temp[start_point*288+i] << 8;
					*(processed_pcm+(i*4)) = prev_sp_l+(cur_sp-prev_sp_l)/4;
					*(processed_pcm+(i*4 + 1)) = prev_sp_l+(cur_sp-prev_sp_l)/2;
					*(processed_pcm+(i*4 + 2)) = prev_sp_l+(cur_sp-prev_sp_l)*3/4;
					*(processed_pcm+(i*4 + 3)) = cur_sp;
					prev_sp_l = cur_sp;
				}
				
				if (count)
				{
					pcm.samp_num = count * 4;
					low_sprt_ogg_output(sound_dev, &pcm);
				}

			}
			else
			{
				snd_set_duplicate(sound_dev, SND_DUP_NONE);
				for (start_point = 0; start_point < full_count; start_point++)
				{
					for(i=0; i<288; i++)
					{
						cur_sp = (INT32)out_buf_temp[start_point*288*ogg_file->channel+i*ogg_file->channel] << 8;
						*(processed_pcm+(i*4)) = prev_sp_l+(cur_sp-prev_sp_l)/4;
						*(processed_pcm+(i*4 + 1)) = prev_sp_l+(cur_sp-prev_sp_l)/2;
						*(processed_pcm+(i*4 + 2)) = prev_sp_l+(cur_sp-prev_sp_l)*3/4;
						*(processed_pcm+(i*4 + 3)) = cur_sp;
						prev_sp_l = cur_sp;
						
						cur_sp = (INT32)out_buf_temp[start_point*288*ogg_file->channel+i*ogg_file->channel+1] << 8;
				 		*(processed_pcm + 1152 + (i*4)) = prev_sp_r+(cur_sp-prev_sp_r)/4;
						*(processed_pcm + 1152 + (i*4 + 1))= prev_sp_r+(cur_sp-prev_sp_r)/2;
						*(processed_pcm + 1152 + (i*4 + 2))= prev_sp_r+(cur_sp-prev_sp_r)*3/4;
						*(processed_pcm + 1152 + (i*4 + 3))= cur_sp;
						prev_sp_r = cur_sp;
					}
					pcm.samp_num = 1152;
					low_sprt_ogg_output(sound_dev, &pcm);
				}

				for (i=0; i<count; ++i)
				{
					cur_sp = (INT32)out_buf_temp[start_point*288*ogg_file->channel+i*ogg_file->channel] << 8;
					*(processed_pcm+(i*4)) = prev_sp_l+(cur_sp-prev_sp_l)/4;
					*(processed_pcm+(i*4 + 1)) = prev_sp_l+(cur_sp-prev_sp_l)/2;
					*(processed_pcm+(i*4 + 2)) = prev_sp_l+(cur_sp-prev_sp_l)*3/4;
					*(processed_pcm+(i*4 + 3)) = cur_sp;
					prev_sp_l = cur_sp;
						
					cur_sp = (INT32)out_buf_temp[start_point*288*ogg_file->channel+i*ogg_file->channel+1] << 8;
				 	*(processed_pcm + 1152 + (i*4)) = prev_sp_r+(cur_sp-prev_sp_r)/4;
					*(processed_pcm + 1152 + (i*4 + 1))= prev_sp_r+(cur_sp-prev_sp_r)/2;
					*(processed_pcm + 1152 + (i*4 + 2))= prev_sp_r+(cur_sp-prev_sp_r)*3/4;
					*(processed_pcm + 1152 + (i*4 + 3))= cur_sp;
					prev_sp_r = cur_sp;
				}

				if (count)
				{
					pcm.samp_num = count * 4;
					low_sprt_ogg_output(sound_dev, &pcm);
				}
			}

		}
		if(bytes_left > 0)
        {
            MEMCPY((char*)(g_temp_pcm_buff),(char*)(ogg_file->pcm_out_buff+(bytes_readed-bytes_left)),bytes_left);
            g_pcm_current_using = bytes_left;
            g_pcm_sample_num = bytes_left/ogg_file->channel/2;
        }
		return;
	}
	
	//normal samprate
	if(pcm.ch_num == 1) //(SINGLE_CHANNEL==ogg_file->aud_mode)
	{
		snd_set_duplicate(sound_dev, SND_DUP_L);
		for(i=0;i<out_samp_num;i++)
		{
			*(processed_pcm + i) = (INT32)out_buf_temp[i] << 8;
		}
	
	}
	else
	{
		snd_set_duplicate(sound_dev, SND_DUP_NONE);
		for(i=0;i<out_samp_num;i++)
		{
			*(processed_pcm + i) = (INT32)out_buf_temp[i*ogg_file->channel] << 8;
	 		*(processed_pcm + 1152 + i) = (INT32)out_buf_temp[i*ogg_file->channel+1] << 8;
		}
	}
	ogg_dup_samplerate = ogg_file->samplerate;
#endif 
    
    if(bytes_left > 0)
    {
        MEMCPY((char*)(g_temp_pcm_buff),(char*)(ogg_file->pcm_out_buff+(bytes_readed-bytes_left)),bytes_left);
        g_pcm_current_using = bytes_left;
        g_pcm_sample_num = bytes_left/ogg_file->channel/2;
    }
    
}
/////////////////////////////////////////////////////////////////////////////
// ogg_play_task
//
// Description:
//	The ogg play task. It fetch the data from the file. Then fill it to the decoder buffer.
//  When reaching the end of the file, it calls the call back function to notify the app.
//
// Arguments:
//	IN  mp_callback_func cb  	The pointer to call back function
//	
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////

static void ogg_play_task(mp_callback_func cb)
{
	long							ret = 0;
	int							current_section = 0;
	double						time_seek_to = 0;
	vorbis_info					*vi;
	struct snd_device				*sound_dev;
	int							*processed_pcm;
	double						song_time;
	
	PE_DBG_PRINTF("MusicEngine: ==> ogg_play_task()! \n");

	sound_dev = (struct snd_device*)dev_get_by_type(NULL, HLD_DEV_TYPE_SND);
	processed_pcm = (int*)ogg_file->processed_pcm_buff;

	if (ov_open(ogg_file->file, &ogg_file->vf, NULL, 0) < 0)
	{
		PE_DBG_PRINTF("MusicEngine: ogg_play_task() ov_open failed! \n");
#ifdef ENABLE_PE_CACHE
		pe_cache_close(ogg_cache_id);//ogg_cache_id is got in play_file
		ogg_cache_id = -1;
#else
		fclose(ogg_file->file);
#endif
		//FREE(processed_pcm);
		osal_flag_set(ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_UNSUCCESSFUL);
		return;
	}
	else
	{
		PE_DBG_PRINTF("MusicEngine: ov_opened \n");

		{

		vorbis_info *vi = ov_info(&ogg_file->vf, -1);
		if (!vi)
		{
			PE_DBG_PRINTF("MusicEngine: ov_info failed!\n");
			ov_clear(&ogg_file->vf);
			osal_flag_set(ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_UNSUCCESSFUL);
			return;
		}
		ogg_file->samplerate = vi->rate;
		ogg_file->channel = vi->channels;
        if(ogg_file->channel>2)//music output do not support multi_channel(>2) file by Ivan 2012/10/26
        {
#ifdef ENABLE_PE_CACHE
            pe_cache_close(ogg_cache_id);//ogg_cache_id is got in play_file
            ogg_cache_id = -1;
#else
            fclose(ogg_file->file);
#endif
            //FREE(processed_pcm);
            osal_flag_set(ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_UNSUCCESSFUL);
            return;
        }
		PE_DBG_PRINTF("\nBitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
		song_time = ov_time_total(&ogg_file->vf, -1);
		if (song_time <= 0)
		{
			PE_DBG_PRINTF("MusicEngine: ov_info failed!\n");
			ov_clear(&ogg_file->vf);
			osal_flag_set(ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_UNSUCCESSFUL);
		}
		
		}

		osal_flag_set(ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_SUCCESS);
	}

	//all work success, we can start to play
	while (ogg_file->command  != OGG_CMD_STOP)
	{
		if (ogg_file->seek_to != 0)
		{
			time_seek_to = ov_time_tell(&ogg_file->vf);
			time_seek_to = time_seek_to + (ogg_file->seek_to * 1000);
	
			if (time_seek_to < 0)
			{
				time_seek_to = 0;
			}
			else if (time_seek_to > song_time)
			{
				time_seek_to = song_time;
			}

			ret = ov_time_seek(&ogg_file->vf, time_seek_to);
			if (ret < 0)
			{
				//seek failed
			}
			osal_task_dispatch_off();
			ogg_file->seek_to = 0;
			osal_task_dispatch_on();
		}

		if (ogg_file->command  == OGG_CMD_NONE)
		{
			ret = ov_read(&ogg_file->vf, (void *)ogg_file->pcm_out_buff, 2304, &current_section);
			if (ret == 0)
			{
				PE_DBG_PRINTF("file end!\n");
				//EOF we need call back
				
							
				osal_task_dispatch_off();
				if (ogg_file->command != OGG_CMD_STOP)
				{
					ogg_file->command  = OGG_CMD_WAIT_FOR_STOP;
				}
				osal_task_dispatch_on();
				
				cb(MP_MUSIC_PLAYBACK_END, 0);
							
				osal_task_sleep(10);
			}
			else if (ret < 0)
			{ 
				PE_DBG_PRINTF("error!!!\n");			
				/* error in the stream.  Not a problem, just reporting it in case we (the app) cares.  In this case, we don't. */

				osal_task_dispatch_off();
				if (ogg_file->command != OGG_CMD_STOP)
				{
					ogg_file->command  = OGG_CMD_WAIT_FOR_STOP;
				}
				osal_task_dispatch_on();
				
				cb(MP_MUSIC_PLAYBACK_END, 0);

				osal_task_sleep(10);
			}
			else
			{
				/* we don't bother dealing with sample rate changes, etc, but you'll have to*/
				   if(ALI_M3327C == sys_ic_get_chip_id() ||ALI_M3101 == sys_ic_get_chip_id()||ALI_M3327 == sys_ic_get_chip_id()||ALI_S3281 == sys_ic_get_chip_id())
					 deca_process_pcm_samples(ret, ogg_file->pcm_out_buff, ogg_file->samplerate, ogg_file->channel, 16);
				   else    //36XX will go this way,if not,please check sys_ic_get_chip_id   //mike
				        process_ogg_pcm(sound_dev, ret, processed_pcm);
				
			}
		}
		else
		{
			osal_task_sleep(10);
		}

	}

	ov_clear(&ogg_file->vf);
	ogg_avoid_under_run = 0;
//	snd_io_control(sound_dev, SND_CC_MUTE, 0);
//	snd_stop(sound_dev);
	//FREE(processed_pcm);
	osal_flag_set(ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_TASK_EXIT);//task end, send 'EXIT' MSG
	PE_DBG_PRINTF("MusicEngine: <== ogg_play_task()! \n");
	
}

/////////////////////////////////////////////////////////////////////////////
// ogg_play_file
//
// Description:
//	To play a song
//
// Arguments:
//  IN  char *filename			The file name
//	IN  mp_callback_func cb  	The pointer to call back function
//	
// Return Value:
//	-1				Unsuccessful
//   0				Success
//
/////////////////////////////////////////////////////////////////////////////

static int ogg_play_file(char *filename, mp_callback_func cb)
{
	OSAL_T_CTSK										ogg_engine_play_task_attribute;
	unsigned long											ogg_engine_flag;

	PE_DBG_PRINTF("MusicEngine: ==> ogg_play_file()! \n");

	//check parameters
	if ((!filename) || (!cb) || (!ogg_file))
	{
		PE_DBG_PRINTF("MusicEngine: ogg_play_file() invalid parameter filename or cb or ogg_file! \n");
		return -1;
	}

	
	//init ogg_file
#ifdef ENABLE_PE_CACHE
	ogg_cache_id = pe_cache_open(filename, NULL, OGG_CACHE_SIZE, OGG_BLOCK_SIZE);
	if(ogg_cache_id < 0)
	{
		PE_DBG_PRINTF("MusicEngine: ogg_play_file() pe_cache_open failed! \n");
		return -1;
	}
	ogg_file->file = (FILE *)((UINT32)ogg_cache_id | PE_CACHE_ID_TAG);//after this action,we will use the pe_cache API in FS
#else
	ogg_file->file = fopen(filename, "rb");
	if (!ogg_file->file)
	{
		PE_DBG_PRINTF("MusicEngine: ogg_play_file() fopen failed! \n");
		return -1;
	}
#endif

	ogg_file->seek_to = 0;
	ogg_file->command = 0;
	ogg_file->stop_imm = TRUE;

	ogg_engine.ogg_engine_flag_id = OSAL_INVALID_ID;
	ogg_engine.ogg_engine_task_id = OSAL_INVALID_ID;

    deca_io_control((struct deca_device *)dev_get_by_id(HLD_DEV_TYPE_DECA, 0), DECA_SET_STR_TYPE, AUDIO_OGG );
    deca_start((struct deca_device *)dev_get_by_id(HLD_DEV_TYPE_DECA, 0),0);

	prev_sp_l = 0;
	prev_sp_r = 0;
    g_pcm_sample_num = 0;
    g_pcm_current_using = 0;
    g_temp_pcm_buff = (char *)(ogg_file->processed_pcm_buff+ON_PCM_BUFF_LEN*8);

	//code to create flag
	ogg_engine_flag = OGG_ENGINE_FLAG_INITIALIZE;
	ogg_engine.ogg_engine_flag_id = osal_flag_create(ogg_engine_flag);
	if ( ogg_engine.ogg_engine_flag_id == OSAL_INVALID_ID)
	{
		PE_DBG_PRINTF("MusicEngine: ogg_play_file() osal_flag_create failure! \n");
#ifdef ENABLE_PE_CACHE
		pe_cache_close(ogg_cache_id);
		ogg_cache_id = -1;
#else
		fclose(ogg_file->file);
#endif
		return -1;
	}


	//start thread
	ogg_engine_play_task_attribute.stksz	= OGG_ENGINE_PLAY_TASK_STACKSIZE*8;
	ogg_engine_play_task_attribute.quantum	= OGG_ENGINE_PLAY_TASK_QUANTUM;
	ogg_engine_play_task_attribute.itskpri	= OSAL_PRI_HIGH;//OSAL_PRI_NORMAL ;
	ogg_engine_play_task_attribute.para1	= (unsigned long)cb;
	//ogg_engine_play_task_attribute.para2	=;
	ogg_engine_play_task_attribute.name[0]	= 'O';
	ogg_engine_play_task_attribute.name[1]	= 'G';
	ogg_engine_play_task_attribute.name[2]	= 'G';
	ogg_engine_play_task_attribute.task = (FP)ogg_play_task;
	ogg_engine.ogg_engine_task_id = osal_task_create(&ogg_engine_play_task_attribute);
	if(ogg_engine.ogg_engine_task_id == OSAL_INVALID_ID)
	{
		PE_DBG_PRINTF("MusicEngine: ogg_play_file() osal_task_create failed! \n");
		
		osal_flag_delete(ogg_engine.ogg_engine_flag_id);
		ogg_engine.ogg_engine_flag_id = OSAL_INVALID_ID;

#ifdef ENABLE_PE_CACHE
		pe_cache_close(ogg_cache_id);
		ogg_cache_id = -1;
#else
		fclose(ogg_file->file);
#endif
		return -1;
	}

	//code to sync
	osal_flag_wait(&ogg_engine_flag, ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_MASK, OSAL_TWF_ORW | OSAL_TWF_CLR, OSAL_WAIT_FOREVER_TIME);
	if (ogg_engine_flag != OGG_ENGINE_FLAG_SUCCESS)
	{		
		osal_flag_delete(ogg_engine.ogg_engine_flag_id);
		ogg_engine.ogg_engine_flag_id = OSAL_INVALID_ID;

		//fclose(ogg_file->file);

		return -1;
	}
	
	PE_DBG_PRINTF("MusicEngine: <== ogg_play_file()! \n");
	return 0;

}

/////////////////////////////////////////////////////////////////////////////
// ogg_stop
//
// Description:
//	To stop current playing song
//
// Arguments:
//  None
//
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////

static void ogg_stop(void)
{
	unsigned long									ogg_engine_flag;
	if (ogg_file && ogg_file->command != OGG_CMD_STOP)
	{
		osal_task_dispatch_off();
		ogg_file->command = OGG_CMD_STOP;
		osal_task_dispatch_on();
		
		osal_flag_wait(&ogg_engine_flag, ogg_engine.ogg_engine_flag_id, OGG_ENGINE_FLAG_MASK, OSAL_TWF_ORW | OSAL_TWF_CLR, OSAL_WAIT_FOREVER_TIME);
		deca_stop((struct deca_device *)dev_get_by_id(HLD_DEV_TYPE_DECA, 0), 0, ADEC_STOP_IMM);
		
		ogg_engine.ogg_engine_task_id = OSAL_INVALID_ID;
		osal_flag_delete(ogg_engine.ogg_engine_flag_id);
		ogg_engine.ogg_engine_flag_id = OSAL_INVALID_ID;

	}
}

/////////////////////////////////////////////////////////////////////////////
// ogg_pause
//
// Description:
//	To pause current playing song
//
// Arguments:
//  None
//
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////

static void ogg_pause(BOOL pause)
{
	if (ogg_file)
	{
		if (pause)
		{
			ogg_file->command = OGG_CMD_PAUSE;
		}
		else
		{
			ogg_file->command = OGG_CMD_NONE;
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// ogg_seek
//
// Description:
//	To seek to the time
//
// Arguments:
//  IN  int time				The time you want to seek to
//
// Return Value:
//	None
//
/////////////////////////////////////////////////////////////////////////////

static void ogg_seek(int time)
{
	if (ogg_file)
	{
		osal_task_dispatch_off();
		ogg_file->seek_to = time;
		osal_task_dispatch_on();
		//for sync
		while (ogg_file->seek_to != 0)
		{
			osal_task_sleep(10);
		}		
	}
}

/////////////////////////////////////////////////////////////////////////////
// ogg_get_time
//
// Description:
//	To get the current playing time
//
// Arguments:
//  None
//
// Return Value:
//	time					current playing time
//  -1						can not get the time
//
/////////////////////////////////////////////////////////////////////////////

static int ogg_get_time(void)
{
	int															time = -1;
		
	time = ov_time_tell(&ogg_file->vf)/ 1000;
	return time;
}

/////////////////////////////////////////////////////////////////////////////
// ogg_get_song_info
//
// Description:
//	To get the song infomation
//
// Arguments:
//  IN  char *filename						The file name of the song
//  OUT MusicInfo *music_info				The struct of song infomation
//
// Return Value:
//  -1						Unsuccessful
//   1						ID3 infomation valid
//   2						Playing time valid
//   3						All valid
//
/////////////////////////////////////////////////////////////////////////////

static int ogg_get_song_info(char *filename, MusicInfo *music_info)
{
	int														status = 3;
	FILE														*file;
	unsigned long												file_length;
	int														bitrate;
	double													song_time;
	OggVorbis_File											temp_ogg;
		
	PE_DBG_PRINTF("MusicEngine: ==> ogg_get_song_info()! \n");
	
	
	if ((!filename) || (!music_info) || (!ogg_file))
	{
		PE_DBG_PRINTF("MusicEngine: ogg_get_song_info() invalid parameter filename or music_info or ogg_file!\n");
		return -1;
	}

#ifndef ENABLE_PE_CACHE
	file = fopen(filename, "rb");//get_song_info, to open the file
	if (!file)
	{
		PE_DBG_PRINTF("MusicEngine: ogg_get_song_info() fopen failed! \n");
		return -1;
	}

	fseek(file, 0, SEEK_END);   
	file_length = ftell(file);
	music_info->file_length = file_length;
	fseek(file, 0, SEEK_SET);
#else
    ogg_info_cache_id = pe_cache_open(filename, NULL, OGG_CACHE_SIZE, OGG_BLOCK_SIZE);
    if (0 > ogg_info_cache_id)
    {
        PE_DBG_PRINTF("<%d><%s> pe_cache_open failed!\n", __LINE__, __FUNCTION__);
        return -1;
    }
    file = (FILE *)(((UINT32)ogg_info_cache_id | PE_CACHE_ID_TAG));
    pe_cache_seek(ogg_info_cache_id, 0, SEEK_END);
    file_length = pe_cache_tell(ogg_info_cache_id);
    music_info->file_length = file_length;
    pe_cache_seek(ogg_info_cache_id, 0, SEEK_SET);
#endif
	//if here the ov is used closely, the stdio is used by the FS, NOT the pe_cache
	if(ov_open(file, &temp_ogg, NULL, 0) < 0)
	{
		PE_DBG_PRINTF("Input does not appear to be an Ogg bitstream.\n");
#ifndef ENABLE_PE_CACHE
        fclose(file);
#else
        pe_cache_close(ogg_info_cache_id);
		ogg_info_cache_id = -1;
#endif
 		return -1;
	}
	song_time = ov_time_total(&temp_ogg, -1);
	if (song_time < 0)
	{
		PE_DBG_PRINTF("Can not get song time from Ogg bitstream.\n");
		song_time = 0;		
	}

	music_info->time = song_time / 1000;

	ov_clear(&temp_ogg);// get_song_info end, to close the file

	PE_DBG_PRINTF("MusicEngine: <== ogg_get_song_info()! \n");
	return status;

}

/////////////////////////////////////////////////////////////////////////////
// ogg_get_decoder_info
//
// Description:
//	To get the infomation that comes from the decoder of the current playing song
//
// Arguments:
//  IN  char *filename							The file name of the song
//  OUT DecoderInfo *decoder_info				The information from returned from decoder
//
// Return Value:
//  -1						Unsuccessful
//	 0						Success
//
/////////////////////////////////////////////////////////////////////////////

static int ogg_get_decoder_info(char *filename, DecoderInfo *decoder_info)
{
	if (!ogg_file)
	{
		return -1;
	}
	
	decoder_info->bit_rate = 0;
	decoder_info->sample_rate= 0;
	decoder_info->channel_mode= 0;
	return 0;
}
